package com.cx.utils;

import io.lettuce.core.LettuceFutures;

import java.time.Duration;
import java.util.concurrent.*;

/**
 * Created on 2016/9/28.
 *
 * @author Alan Shu
 */
public class CacheResult {

    private static Duration DEFAULT_TIMEOUT = CacheConsts.ASYNC_RESULT_TIMEOUT;

    private CacheResultCode resultCode;
    private String message;
    private Object data;
    private CompletionStage<ResultData> future;

    private Duration timeout = DEFAULT_TIMEOUT;

    public CacheResult(CompletionStage<ResultData> future) {
        this.future = future;
    }

    public CacheResult(CacheResultCode resultCode, String message) {
        this(CompletableFuture.completedFuture(new ResultData(resultCode, message, null)));
    }

    public CacheResult(Throwable ex) {
        future = CompletableFuture.completedFuture(new ResultData(ex));
    }

    public boolean isSuccess() {
        return getResultCode() == CacheResultCode.SUCCESS;
    }

    public boolean isNeedRetry() {
        resultCode = getResultCode();
        if(null == resultCode){
            resultCode = CacheResultCode.DISABLE_INVOKED;
        }
        return resultCode == CacheResultCode.FAIL || resultCode == CacheResultCode.PART_SUCCESS;
    }

    private void waitForResult() {
        if (resultCode != null || resultCode == CacheResultCode.DISABLE_INVOKED) {
            return;
        }

        //future的侦听器接收结果和错误对象
//        future.handle(new BiFunction<ResultData, Throwable, ResultData>() {
//            @Override
//            public ResultData apply(ResultData resultData, Throwable throwable) {
//
//                if(null != throwable){
//                    if (throwable instanceof IllegalStateException || throwable instanceof CompletionException) {
//                        resultCode = CacheResultCode.DISABLE_INVOKED;
//                    }
//
//                    fetchResultFail(throwable);
//                    return new ResultData(throwable);
//                }
//
//                return resultData;
//            }
//        }).thenAccept(new Consumer<ResultData>() {
//            @Override
//            public void accept(ResultData resultData) {
//                fetchResultSuccess(resultData);
//            }
//        });

        final CompletableFuture<ResultData> response = future.toCompletableFuture();

        //等待超时的response
        try {
            ResultData resultData = response.exceptionally(throwable -> new ResultData(throwable)).get(timeout.toMillis(), TimeUnit.MILLISECONDS);
            fetchResultSuccess(resultData);
        } catch (TimeoutException | InterruptedException | ExecutionException e) {
            fetchResultFail(e);
        }

        if(null == resultCode){

            //循环等待直到所有的futures完成
            while (!response.isDone()){
                LettuceFutures.awaitAll(CacheConsts.ADD_ASYNC_RESULT_MILLIS, TimeUnit.MILLISECONDS, response);
            }
        }
    }

    protected void fetchResultSuccess(ResultData resultData) {
        resultCode = resultData.getResultCode();
        message = resultData.getMessage();
        data = resultData.getData();
    }

    protected void fetchResultFail(Throwable e) {
        if(e instanceof java.lang.IllegalStateException || e instanceof CompletionException || e instanceof TimeoutException){
            this.resultCode = CacheResultCode.DISABLE_INVOKED;
        }else {
            this.resultCode = CacheResultCode.FAIL;
        }
        message = e.getClass() + ":" + e.getMessage();
    }

    public CacheResultCode getResultCode() {
        waitForResult();
        return resultCode;
    }

    public String getMessage() {
        waitForResult();
        return message;
    }

    public Object getData() {
        waitForResult();
        return data;
    }

    public CompletionStage<ResultData> future() {
        return future;
    }

    public static void setDefaultTimeout(Duration defaultTimeout) {
        DEFAULT_TIMEOUT = defaultTimeout;
    }

    public void setTimeout(Duration timeout) {
        this.timeout = timeout;
    }
}
